import {
  Object3D, Vector2, Vector3, Raycaster, MOUSE, Frustum,
  Scene, Color, OrthographicCamera, WebGLRenderer, MeshLambertMaterial,
  DoubleSide, Mesh, AmbientLight, PointLight, Group, FileLoader,
  LineBasicMaterial, LineSegments, EdgesGeometry, SphereBufferGeometry,
  LineSegments2, LineMaterial, LineSegmentsGeometry, MeshBasicMaterial, DirectionalLight, Box3
} from './three.custom.js';
import { clearObject3D, unique, updateConfig, selectionBoxHelper, createCoordinateSystem } from './three.util.js'
import { GLTFLoader } from './GLTFLoader.js';
import CameraSpinControls from '../spin-controls/CameraSpinControls';
import threeConfig from '../../config/three.config';

// containerIn：渲染区域   outputElementSelection：选中单元输出
function createThree(parm_container, parm_outputElementSelection) {
  // 定义常量
  // 三维模型状态，与cameraSpinControls.js中的保持一致
  const STATE = { NONE: 0, ROTATE: 1, DOLLY: 2, PAN: 3, TOUCH_DOLLY_PAN: 4 };
  // 相机距离物体的距离，相对于物体的多少倍，若数值较小当旋转物体时容易接触相机造成部分不可见
  // 若数值较大，相机距离物体超过了相机的视椎体远端面，则会造成物体不渲染不可见（也可通过加大相机的视椎体远端面的参数解决）
  const CAMERA_OBJ_DISTANCE = 20;
  // 区分点击和拖动的参数：鼠标移动的width+height的最小距离（px）
  const CLICK_DRAG_DISTANCE = 3;
  // 是否启用阻尼（惯性），启用后会给旋转物体带来重量感，旋转后不会立即停下，而是缓慢停下，默认不启用
  const ENABLE_DAMPING = threeConfig.enableDamping ? threeConfig.enableDamping : false;
  // 配置鼠标按键组合
  const DEFAULT_MOUSE_KEYS = {
    ROTATE: { mouse: MOUSE.RIGHT, ctrlKey: true, shiftKey: false, altKey: false }, // 配置旋转
    DOLLY: { mouse: null, ctrlKey: false, shiftKey: false, altKey: false }, // 配置缩放
    PAN: { mouse: MOUSE.MIDDLE, ctrlKey: false, shiftKey: false, altKey: false }, // 配置平移
  };
  const MOUSE_KEYS = updateConfig(threeConfig.mousekeys, DEFAULT_MOUSE_KEYS);
  const SELECT_MOUSE = MOUSE.LEFT; // 配置哪个鼠标键进行选择
  const MULTI_SELECT_KEY = { ctrlKey: false, shiftKey: true, altKey: false }; // 配置SELECT_MOUSE+哪个按键进行多选
  const DESELECT_KEY = { ctrlKey: true, shiftKey: false, altKey: false }; // 配置SELECT_MOUSE+哪个按键进行取消选择
  const SET_ROTATE_CENTER_MOUSE = MOUSE.RIGHT; // 配置哪个鼠标键进行旋转中心的设置
  // 模型名字
  const FACE_OBJ_NAME = 'faceObj';
  const LINE_OBJ_NAME = 'lineObj';
  const EDGE_LINE_OBJ_NAME = 'edgeLineObj';
  const MAIN_OBJ_NAME = 'mainObj';
  const TRACKBALL_WIDGET_NAME = 'trackballWidget'
  // 事件名称
  const MOUSE_DOWN_EVENT_NAME = 'mousedown';
  const MOUSE_MOVE_EVENT_NAME = 'mousemove';
  const MOUSE_UP_EVENT_NAME = 'mouseup';
  // 颜色
  const HOVER_COLOR = 0x00ff00;
  const HOVER_LINE_COLOR = new Color(0x00ff00);
  // const CHOOSE_FACE_COLOR = 0x0000f0;
  const CHOOSE_FACE_COLOR = '#347ecc';//#163555

  const CHOOSE_LINE_COLOR = new Color(0x0000ff);
  // const BACKGROUND_COLOR = '#262626';
  const BACKGROUND_COLOR = '#f7f7f7';
  // 粗线材质
  const initLineMaterial = new LineMaterial({
    linewidth: 3, // in pixels
    vertexColors: true
  });

  // 定义变量
  let container = parm_container;
  let outputElementSelection = parm_outputElementSelection;
  let camera = null;
  let scene = null;
  let renderer = null;
  let cameraSpinControl = null;
  let loop = null;
  let faceElementXref = []; // 面-element对应信息
  let elementFaceXref = {}; // element-面对应信息
  let lineElementXref = []; // 线-element对应信息
  let nodesXref = [];//坐标点信息集合
  let elementLineXref = {}; // element-线对应信息
  let elementCenterXref = {}; // element-center对应信息
  let gltfLoader = new GLTFLoader();
  let fileLoader = new FileLoader();
  let state = STATE.NONE; // 当前三维模型的状态
  // 设置旋转中心相关
  let currentRotateCenter = null; // 当前设置的旋转中心，为null表示默认的旋转中心：屏幕中心
  let startPoint = new Vector2();
  // 选择相关
  let raycaster = new Raycaster();
  // raycaster.params.Line.threshold = 10;
  let frustum = new Frustum();
  let selection = [];
  let isDown = false; // 选择鼠标键（如左键）是否按下
  let isMouseDown = false; // 鼠标是否按下
  let selectionBoxElement = null;
  let BoxStartPoint = new Vector2();
  let BoxEndPoint = new Vector2();
  // 全局坐标系相关
  let coordinateSystemScene = null;
  let coordinateSystemCamera = null;
  let coordinateSystemRenderer = null;
  let cameraWorldDirection = new Vector3();
  // 单元收缩显示相关  即缝隙效果
  let attributesPosition = null; // 备份模型初始attributes
  // 粗线相关
  let lineGeometryIndexed = null; // 带index的 lineGeometry
  let lineGeometryInitColor = null;
  let lineGeometryCurrentColor = null; // lineGeometry当前的颜色数组（包括初始颜色和选择颜色）

  // 初始化
  _init();

  // 监控渲染区域container变化
  const resezeObserver = new ResizeObserver(() => { _onWindowResize() });
  resezeObserver.observe(container);

  // ------------------------------------------------------------------------
  // public methods 外部可调用
  // ------------------------------------------------------------------------

  let ctx = new Object();
  // 加载gltf模型
  ctx.loadGltf = async function loadGltf(modelInfo) {
    if (modelInfo.userDataUrl) {
      await _initUserData(modelInfo.userDataUrl).catch(() => { });
    }
    await _initModel(modelInfo.url);
    // 设置相机初始角度为轴测图角度
    _showView(new Vector3(1, 1, 1));
    _animate();
    console.log('渲染完成');
  }

  // 重新加载，场景中其他状态保持不变
  ctx.reloadGltf = async function reloadGltf(modelInfo) {
    if (modelInfo.userDataUrl) {
      await _initUserData(modelInfo.userDataUrl).catch(() => { });
    }
    await _initModel(modelInfo.url);
    // 高亮选中的单元
    _highlightSelection(selection);
    _animate();
    console.log('重新渲染完成');
  }

  // 工具条事件
  ctx.toolbarAction = function toolbarAction(action, value) {
    if (action === "frontView") {
      _showView(new Vector3(0, 0, 1)); // 主视图/前视图视角 y⬅⬆x
    }
    if (action === "backView") {
      _showView(new Vector3(0, 0, -1)); // 后视图视角 x⬆→y
    }
    if (action === "topView") {
      _showView(new Vector3(0, 1, 0)); // 俯视图视角 z⬇→x
    }
    if (action === "bottomView") {
      _showView(new Vector3(0, -1, 0)); // 仰视图视角 z⬆→x
    }
    if (action === "leftView") {
      _showView(new Vector3(-1, 0, 0)); // 左视图视角 y⬆→z
    }
    if (action === "rightView") {
      _showView(new Vector3(1, 0, 0)); // 右视角视图 z⬅⬆y
    }
    if (action === "axonometricView") {
      _showView(new Vector3(1, 1, 1)); // 轴测图视角 xyz
    }
    if (action === "default") {
      _setRotateCenterMode(false);
    }
    if (action === "rotateCenter") {
      _setRotateCenterMode(true);
    }
    if (action === "shrink") {
      _shrink(value); // 单元收缩显示
    }
  }

  // 设置选中单元
  ctx.setSelection = function setSelection(selectionIn) {
    if (scene.getObjectByName(FACE_OBJ_NAME) !== undefined) {
      selection = selectionIn;
      // 高亮选中的单元
      _highlightSelection(selection);
    }
  }

  // 清理模型
  ctx.clear = function clear() {
    clearObject3D(scene.getObjectByName(MAIN_OBJ_NAME));
    faceElementXref = [];
    elementFaceXref = {};
    lineElementXref = [];
    nodesXref = [];
    elementLineXref = {};
    elementCenterXref = {};
    renderer.dispose();
  }

  // 清缓存，dispose后需要创建新实例才可运行
  ctx.dispose = function dispose() {
    clearObject3D(scene);
    clearObject3D(coordinateSystemScene);
    renderer.dispose();
    renderer.forceContextLoss();
    // renderer.content = null; //网上抄的，没有发现实质性作用，console.log(renderer.content)显示undefined
    renderer.domElement.remove(); // 删除renderer中新建的canvas元素
    renderer.domElement = null;
    renderer = null; // 这里面有一个buffergeometry，退出时需清除
    coordinateSystemRenderer.dispose();
    coordinateSystemRenderer.forceContextLoss();
    coordinateSystemRenderer.domElement.remove();
    coordinateSystemRenderer.domElement = null;
    coordinateSystemRenderer = null;
    cancelAnimationFrame(loop);
    faceElementXref = null;
    elementFaceXref = null;
    lineElementXref = null;
    nodesXref = null;
    elementLineXref = null;
    elementCenterXref = null;
  }

  // 重新绑定参数
  ctx.applyArguments = function applyArguments(parm_container, parm_outputElementSelection) {
    container = parm_container;
    // 重新绑定renderer
    container.appendChild(renderer.domElement);
    container.appendChild(coordinateSystemRenderer.domElement);
    // 重新绑定resezeObserver
    resezeObserver.disconnect();
    resezeObserver.observe(container);
    // 重新绑定_outputElementSelection
    outputElementSelection = parm_outputElementSelection;
  }

  return ctx;

  // ------------------------------------------------------------------------
  // internals 内部使用
  // ------------------------------------------------------------------------

  // 初始化
  function _init() {
    // 定义场景 scene
    scene = new Scene();
    scene.background = new Color(BACKGROUND_COLOR);

    // 定义相机 camera
    // 使用正交相机
    camera = new OrthographicCamera(container.clientWidth / -2, container.clientWidth / 2, container.clientHeight / 2, container.clientHeight / -2, 0, 1000000);
    camera.updateProjectionMatrix();
    // 设置光源
    camera.add(new AmbientLight('#dddddd'));
    const pointLight = new PointLight('#666666');
    pointLight.distance = 1000000;
    pointLight.intensiity = 5;
    camera.add(pointLight);
    scene.add(camera);

    // 定义渲染器 renderer
    renderer = new WebGLRenderer({ antialias: true }); // 抗锯齿antialias
    renderer.setSize(container.clientWidth, container.clientHeight);
    renderer.domElement.style.width = '100%';
    renderer.domElement.style.height = '100%';
    container.appendChild(renderer.domElement);
    renderer.sortObjects = false; // 物体添加到场景中的顺序决定了物体渲染顺序

    // 定义控制器 cameraSpinControl
    cameraSpinControl = new CameraSpinControls(camera, renderer.domElement);
    if (camera.isOrthographicCamera) {
      // With orthographic projection calculations are more direct使正交相机下旋转更顺滑
      // using POINTER_SPHERE_MAPPING.HOLROYD or SHOEMAKE
      cameraSpinControl.spinControl.setPointerToSphereMapping(
        cameraSpinControl.spinControl.POINTER_SPHERE_MAPPING.SHOEMAKE
      );
    }
    cameraSpinControl.startTrackballScreenCenter = false;
    cameraSpinControl.setEnableDamping(ENABLE_DAMPING); // 配置是否启用阻尼（惯性）
    cameraSpinControl.setMouseKeys(MOUSE_KEYS); // 配置控制器中的mouse keys
    // 监控控制器变化
    cameraSpinControl.addEventListener('start', (event) => { state = event.state });
    cameraSpinControl.addEventListener('change', () => {
      // 平移时旋转中心点跟着物体走
      if (state === STATE.PAN) {
        if (currentRotateCenter !== null) {
          cameraSpinControl.setTargetPosition(currentRotateCenter);
        }
      }
    });
    cameraSpinControl.addEventListener('end', (event) => { state = event.state });

    // 定义全局坐标系
    coordinateSystemScene = new Scene();
    // coordinateSystemScene.add(createCoordinateSystem(0.5));
    coordinateSystemCamera = new OrthographicCamera(-1, 1, 1, -1, 0, 1000);
    coordinateSystemRenderer = new WebGLRenderer({ antialias: true, alpha: true });
    coordinateSystemRenderer.setSize(100, 100);
    coordinateSystemRenderer.domElement.style.position = 'absolute';
    coordinateSystemRenderer.domElement.style.left = '0px';
    coordinateSystemRenderer.domElement.style.bottom = '0px';
    container.appendChild(coordinateSystemRenderer.domElement);

    // 定义跟踪球UI
    const trackballWidget = new Group();
    trackballWidget.name = TRACKBALL_WIDGET_NAME;
    const geometry = new SphereBufferGeometry(6, 8, 8);
    const meshMaterial = new MeshLambertMaterial({ color: 0xde2df0 });
    const lineMaterial = new LineBasicMaterial({ color: 0x952df0 });
    trackballWidget.add(new LineSegments(new EdgesGeometry(geometry), lineMaterial));
    trackballWidget.add(new Mesh(geometry, meshMaterial));
    scene.add(trackballWidget);
    trackballWidget.visible = false;

    // 定义几何体
    const obj = new Object3D();
    obj.name = MAIN_OBJ_NAME;
    scene.add(obj);

    renderer.render(scene, camera); // 渲染画布
    _setEnableSelect(true); // 允许选择
    selectionBoxElement = selectionBoxHelper(); //构造框选框div
  }

  // 更新视锥体
  function _updateFrustum(startPoint, endPoint) {
    // Avoid invalid frustum  Number.EPSILON
    if (startPoint.x === endPoint.x) {
      endPoint.x += 0.00001;
    }
    if (startPoint.y === endPoint.y) {
      endPoint.y += 0.00001;
    }

    camera.updateProjectionMatrix();

    if (camera.isOrthographicCamera) {
      // 由2个点构造4个点
      const left = Math.min(startPoint.x, endPoint.x);
      const top = Math.max(startPoint.y, endPoint.y);
      const right = Math.max(startPoint.x, endPoint.x);
      const down = Math.min(startPoint.y, endPoint.y);

      // 由4个点构造8个点
      const vecTopLeft = new Vector3(left, top, -1);
      const vecTopRight = new Vector3(right, top, -1);
      const vecDownRight = new Vector3(right, down, -1);
      const vecDownLeft = new Vector3(left, down, -1);
      const vecFarTopLeft = new Vector3(left, top, 1);
      const vecFarTopRight = new Vector3(right, top, 1);
      const vecFarDownRight = new Vector3(right, down, 1);
      const vecFarDownLeft = new Vector3(left, down, 1);

      // 标准化设备坐标（Normalised Device Coordinates，NDC）=> 世界坐标（World Coordinates）
      vecTopLeft.unproject(camera);
      vecTopRight.unproject(camera);
      vecDownRight.unproject(camera);
      vecDownLeft.unproject(camera);
      vecFarTopLeft.unproject(camera);
      vecFarTopRight.unproject(camera);
      vecFarDownRight.unproject(camera);
      vecFarDownLeft.unproject(camera);

      const planes = frustum.planes;
      planes[0].setFromCoplanarPoints(vecTopLeft, vecFarTopLeft, vecFarTopRight);
      planes[1].setFromCoplanarPoints(vecTopRight, vecFarTopRight, vecFarDownRight);
      planes[2].setFromCoplanarPoints(vecFarDownRight, vecFarDownLeft, vecDownLeft);
      planes[3].setFromCoplanarPoints(vecFarDownLeft, vecFarTopLeft, vecTopLeft);
      planes[4].setFromCoplanarPoints(vecTopRight, vecDownRight, vecDownLeft);
      planes[5].setFromCoplanarPoints(vecFarDownRight, vecFarTopRight, vecFarTopLeft);
      planes[5].normal.multiplyScalar(-1);
    }
  }

  // 获取框选区域单元
  function _getSelectionBoxChild(startPoint, endPoint) {
    // 设置默认值
    const collection = [];
    _updateFrustum(startPoint, endPoint);
    // searchChildInFrustum
    for (const eid in elementCenterXref) {
      const centerVector3 = new Vector3(
        elementCenterXref[eid][0],
        elementCenterXref[eid][1],
        elementCenterXref[eid][2],
      );
      if (frustum.containsPoint(centerVector3.applyMatrix4(scene.getObjectByName(FACE_OBJ_NAME).matrixWorld))) {
        collection.push(eid);
      }
    }
    return collection;
  }

  // 获取点选单元
  function _getSelectionChild(mouse) {
    // 点击选中的element加入数组
    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects(
      [scene.getObjectByName(FACE_OBJ_NAME), scene.getObjectByName(LINE_OBJ_NAME)]
    );
    const collection = [];
    let distance = null;
    if (intersects.length > 0) {
      for (const intersectsId in intersects) {
        // 如果面/线重合，则返回所有的eid
        if (intersectsId == 0) {
          distance = intersects[intersectsId].distance;
        } else if (distance != intersects[intersectsId].distance) {
          break;
        }
        if (intersects[intersectsId].faceIndex !== null) {
          let eid = null;
          // 面
          if (intersects[intersectsId].object.name === FACE_OBJ_NAME) {
            eid = faceElementXref[intersects[intersectsId].faceIndex]; // face to element
          }
          // 线
          else if (intersects[intersectsId].object.name === LINE_OBJ_NAME) {
            eid = lineElementXref[intersects[intersectsId].faceIndex]; // line to element
          }
          if (eid !== undefined) {
            collection.push(eid);
          }
        }
      }
    }
    return collection;
  }

  // 高亮选择单元
  function _highlightSelection(selection) {
    console.log('高亮显示');
    console.log(selection);
    console.log(elementCenterXref[selection[0]]);
    const geometry = scene.getObjectByName(FACE_OBJ_NAME).geometry;
    const lineGeometry = scene.getObjectByName(LINE_OBJ_NAME).geometry;
    // 取消所有高亮
    geometry.clearGroups();
    geometry.addGroup(0, geometry.index.count, 0);
    let colors = [];
    for (let i = 0; i < lineGeometryIndexed.index.count; i++) {
      colors.push(lineGeometryInitColor.r, lineGeometryInitColor.g, lineGeometryInitColor.b);
    }
    // 高亮目前选中的部分
    for (const item of selection) {
      // 面
      if (elementFaceXref[item] != undefined) {
        for (const i of elementFaceXref[item]) {
          geometry.addGroup(i * 3, 3, 1);
        }
        geometry.groupsNeedUpdate = true;
      }
      // 线
      else if (elementLineXref[item] != undefined) {
        for (const i of elementLineXref[item]) {
          colors[i * 6] = CHOOSE_LINE_COLOR.r;
          colors[i * 6 + 1] = CHOOSE_LINE_COLOR.g;
          colors[i * 6 + 2] = CHOOSE_LINE_COLOR.b;
          colors[i * 6 + 3] = CHOOSE_LINE_COLOR.r;
          colors[i * 6 + 4] = CHOOSE_LINE_COLOR.g;
          colors[i * 6 + 5] = CHOOSE_LINE_COLOR.b;
        }
      }
    }
    lineGeometry.setColors(colors);
    lineGeometryCurrentColor = colors; // 同步当前线的color数组
    // 创建一个精灵点的材质
    if (selection != undefined) {
      scene.children.forEach(child => {
        if (child?.geometry?.type === 'SphereGeometry') {
          scene.remove(child);
          child?.geometry.dispose();
        }
      })
      // 调整跟踪球半径的大小
      const box = new Box3().setFromObject(scene);
      const size = box.getSize(new Vector3()).length();
      const points = nodesXref[selection[0]];
      for (let i = 0; i < points.length; i++) {
        // 创建一个精灵点的几何体
        var sphereGeometry = new SphereBufferGeometry(size / 600, 8, 8); // 半径为0.1，16个经纬度分段
        var sphereMaterial = new MeshBasicMaterial({ color: 0xff0000 }); // 红色的材质

        // 创建精灵点
        var sphere = new Mesh(sphereGeometry, sphereMaterial);
        // 设置精灵点的位置
        sphere.position.set(points[i].xyz[0], points[i].xyz[1], points[i].xyz[2]); // 将x, y, z替换为你想要高亮的点的坐标
        // 将精灵点添加到场景中
        scene.add(sphere);

        // 创建一个平行光源，用作高亮
        var directionalLight = new DirectionalLight(0xffffff);
        directionalLight.position.set(points[i].xyz[0], points[i].xyz[1], points[i].xyz[2]).normalize(); // 将平行光源的位置设置为精灵点的位置
        // 将光源添加到场景中
        scene.add(directionalLight);
      }
      // 渲染场景
      renderer.render(scene, camera);
    }

  }

  // 高亮滑动单元
  function _hoverHighlight(selection) {
    const geometry = scene.getObjectByName(FACE_OBJ_NAME).geometry;
    const lineGeometry = scene.getObjectByName(LINE_OBJ_NAME).geometry;
    // 取消滑动高亮
    while (geometry.groups[geometry.groups.length - 1].materialIndex == 2) {
      geometry.groups.splice(geometry.groups.length - 1, 1);
    }
    let colors = JSON.parse(JSON.stringify(lineGeometryCurrentColor));
    // 高亮目前滑动的部分
    for (const item of selection) {
      // 面
      if (elementFaceXref[item] != undefined) {
        for (const i of elementFaceXref[item]) {
          geometry.addGroup(i * 3, 3, 2);
        }
        geometry.groupsNeedUpdate = true;
      }
      // 线
      else if (elementLineXref[item] != undefined) {
        for (const i of elementLineXref[item]) {
          colors[i * 6] = HOVER_LINE_COLOR.r;
          colors[i * 6 + 1] = HOVER_LINE_COLOR.g;
          colors[i * 6 + 2] = HOVER_LINE_COLOR.b;
          colors[i * 6 + 3] = HOVER_LINE_COLOR.r;
          colors[i * 6 + 4] = HOVER_LINE_COLOR.g;
          colors[i * 6 + 5] = HOVER_LINE_COLOR.b;
        }
      }
    }
    lineGeometry.setColors(colors);
  }

  // 设置是否可选择
  function _setEnableSelect(enabled) {
    if (enabled) {
      _addSelectionEventListener();
    } else {
      _removeSelectionEventListener();
    }
  }

  // 是否进入设置旋转中心模式
  function _setRotateCenterMode(enabled) {
    if (enabled) {
      scene.getObjectByName(TRACKBALL_WIDGET_NAME).visible = true;
      // 添加设置旋转中心事件
      _addSetRotateCenterEventListener();
    } else {
      scene.getObjectByName(TRACKBALL_WIDGET_NAME).visible = false;
      // 移除设置旋转中心事件
      _removeSetRotateCenterEventListener();
      currentRotateCenter = null;

      // 若物体在屏幕中间，则将旋转中心设为物体中心，若物体不在屏幕中心，则将旋转中心设为屏幕中心
      // 将物体中心坐标转标准设备坐标
      const geometryCenter = scene.getObjectByName(FACE_OBJ_NAME).geometry.boundingSphere.center;
      const stdVector = geometryCenter.clone().project(camera);
      // if 坐标约为（0，0）
      if (Math.abs(stdVector.x) < 0.01 && Math.abs(stdVector.y) < 0.01) {
        // 将旋转中心设置为物体中心
        cameraSpinControl.setTargetPosition(geometryCenter);
      } else {
        // 将旋转中心设置为屏幕中心
        cameraSpinControl.target.setFromMatrixPosition(cameraSpinControl.trackballToObject);
        const startDistance = cameraSpinControl.target.length();
        cameraSpinControl.target.set(0, 0, -startDistance);
        cameraSpinControl.target.applyQuaternion(camera.quaternion);
        cameraSpinControl.target.add(camera.position);
        cameraSpinControl.setTargetPosition(cameraSpinControl.target);
      }
    }
  }

  // 多视图视角
  function _showView(viewpoint) {
    const geometry = scene.getObjectByName(FACE_OBJ_NAME).geometry;
    camera.position.set(
      geometry.boundingSphere.center.x + viewpoint.x * CAMERA_OBJ_DISTANCE * geometry.boundingSphere.radius,
      geometry.boundingSphere.center.y + viewpoint.y * CAMERA_OBJ_DISTANCE * geometry.boundingSphere.radius,
      geometry.boundingSphere.center.z + viewpoint.z * CAMERA_OBJ_DISTANCE * geometry.boundingSphere.radius
    );
    camera.lookAt(geometry.boundingSphere.center);
    // 视角的改变不影响旋转中心的变化
    if (currentRotateCenter !== null) {
      cameraSpinControl.setTargetPosition(currentRotateCenter);
    } else {
      cameraSpinControl.setTargetPosition(geometry.boundingSphere.center);
    }
    _fullscreen();
  }

  // 自适应屏幕大小（使用 包围球确定高宽缩放比例 方式）
  function _fullscreen() {
    const maxDiameter = scene.getObjectByName(FACE_OBJ_NAME).geometry.boundingSphere.radius * 2;
    // 画布宽度 是 包围球直径 的widthZoom倍， 即包围球缩放widthZoom倍 宽度刚好填满画布
    const widthZoom = container.clientWidth / maxDiameter;
    // 画布高度 是 包围球直径 的heightZoom倍，即包围球缩放heightZoom倍 高度刚好填满画布
    const heightZoom = container.clientHeight / maxDiameter;
    // 取两者缩放比例较小值，设置为正交相机的缩放倍数
    if (widthZoom >= heightZoom) {
      camera.zoom = heightZoom;
    } else {
      camera.zoom = widthZoom;
    }
    camera.updateProjectionMatrix();
  }

  // 初始化三维模型
  function _initModel(url) {
    scene.children.forEach(child => {
      if (child?.geometry?.type === 'SphereGeometry') {
        scene.remove(child);
        child?.geometry.dispose();
      }
    })
    return new Promise((resolve) => {
      // 加载gltf模型
      gltfLoader.load(url, (gltf) => {
        const faceGeometry = gltf.scene.children[0].geometry;
        lineGeometryIndexed = gltf.scene.children[2].geometry;

        // 材质设置
        // 面
        const initMaterial = gltf.scene.children[0].material; // 面的初始化材质
        // 点击面的材质 0xd83c3c 0x3fb3bf 0x28aaaa vertexColors: THREE.FaceColors
        const chooseMaterial = new MeshLambertMaterial({ color: CHOOSE_FACE_COLOR, side: DoubleSide, transparent: false });
        const hoverMaterial = new MeshLambertMaterial({ color: HOVER_COLOR, side: DoubleSide, transparent: false });
        const faceMaterails = [];
        faceMaterails.push(initMaterial, chooseMaterial, hoverMaterial);
        // 对于多种材质的mesh，需要将所有面放到组中才能渲染出来
        faceGeometry.addGroup(0, faceGeometry.index.count, 0);
        // 线
        const lineGeometryBold = new LineSegmentsGeometry();
        lineGeometryBold.setPositions(lineGeometryIndexed.toNonIndexed().attributes.position.array);
        let colors = [];
        lineGeometryInitColor = gltf.scene.children[2].material.color;
        for (let i = 0; i < lineGeometryIndexed.index.count; i++) {
          colors.push(lineGeometryInitColor.r, lineGeometryInitColor.g, lineGeometryInitColor.b);
        }
        lineGeometryBold.setColors(colors);
        lineGeometryCurrentColor = colors;

        // object
        const face = new Mesh(faceGeometry, faceMaterails);
        face.name = FACE_OBJ_NAME;
        const edgeLine = gltf.scene.children[1];
        edgeLine.name = EDGE_LINE_OBJ_NAME;
        const lineBold = new LineSegments2(lineGeometryBold, initLineMaterial);
        lineBold.name = LINE_OBJ_NAME;
        const obj = scene.getObjectByName(MAIN_OBJ_NAME);
        clearObject3D(obj);
        obj.add(face);
        obj.add(edgeLine);
        obj.add(lineBold);
        console.log('scene', scene);
        // 计算法向量，设置光源材质需要
        faceGeometry.computeVertexNormals();
        // 计算包围盒，设置模型显示角需要
        faceGeometry.computeBoundingBox();
        faceGeometry.computeBoundingSphere();

        // 调整跟踪球半径的大小
        const trackballRadius = faceGeometry.boundingSphere.radius / 200;
        _updateTrackballGeometry(scene.getObjectByName(TRACKBALL_WIDGET_NAME), new SphereBufferGeometry(trackballRadius, 8, 8))

        // 备份原始坐标，用于当坐标改变后还能找回初始坐标
        // 面、线、边缘线三个模型共用同一个attributes.position，只要保存一个即可
        attributesPosition = faceGeometry.attributes.position.clone();
        resolve();
      },
        (xhr) => {
          // 加载进度回调
          console.log('加载进度')
          console.log(Math.round((xhr.loaded / xhr.total) * 100))
        }
      );
    });
  }

  // 更新跟踪球的半径
  function _updateTrackballGeometry(object, geometry) {
    object.children[0].geometry.dispose();
    object.children[1].geometry.dispose();
    object.children[0].geometry = new EdgesGeometry(geometry);
    object.children[1].geometry = geometry;
  }

  // 循环渲染
  function _animate() {
    loop = requestAnimationFrame(_animate);
    cameraSpinControl.update();
    if (scene.getObjectByName(TRACKBALL_WIDGET_NAME)) {
      scene.getObjectByName(TRACKBALL_WIDGET_NAME).position.copy(cameraSpinControl.target);
    }
    // resolution of the viewport
    initLineMaterial.resolution.set(container.clientWidth, container.clientHeight);
    // main scene
    renderer.render(scene, camera);
    // coordinate system scene
    // 因为matrixAutoUpdate=true，所以修改position和quaternion会自动修改矩阵
    camera.getWorldDirection(cameraWorldDirection);
    coordinateSystemCamera.position.set(-cameraWorldDirection.x, -cameraWorldDirection.y, -cameraWorldDirection.z);
    coordinateSystemCamera.quaternion.copy(camera.quaternion);
    coordinateSystemRenderer.render(coordinateSystemScene, coordinateSystemCamera);
  }

  // 初始化用户业务信息
  function _initUserData(url) {
    return new Promise((resolve, reject) => {
      fileLoader.setResponseType('json');
      fileLoader.load(
        url,
        (jsonData) => {
          // 由jsonData生成中心点、face-element、line-element信息
          // 用于选取、shrink等
          elementCenterXref = {};
          elementFaceXref = {};
          faceElementXref = [];
          elementLineXref = {};
          lineElementXref = [];
          nodesXref = [];
          jsonData.item_xref.forEach(item => {
            // 中心点信息
            elementCenterXref[item.item_no] = item.axis_of_center;
            // face-element对应信息
            if (item.face_id != undefined) {
              elementFaceXref[item.item_no] = item.face_id;
              item.face_id.forEach(faceItem => {
                faceElementXref[faceItem] = item.item_no;
              })
            }
            // line-element对应信息
            else if (item.rod_id != undefined) {
              elementLineXref[item.item_no] = item.rod_id;
              item.rod_id.forEach(rodItem => {
                lineElementXref[rodItem] = item.item_no;
              })
            }
            // 面坐标信息
            nodesXref[item.item_no] = item.nodes;
          })
          resolve();
        },
        () => { },
        (err) => { reject(err) }
      )
    });
  }

  // 屏幕坐标（clientX, clientY） => 标准化设备坐标（Normalised Device Coordinates，NDC），相对于中心点
  function clientCoordinatesToNDC(clientX, clientY) {
    return new Vector2(
      ((clientX - renderer.domElement.getBoundingClientRect().left) / renderer.domElement.width) * 2 - 1,
      -((clientY - renderer.domElement.getBoundingClientRect().top) / renderer.domElement.height) * 2 + 1
    )
  }

  // 单元收缩显示，即缝隙效果
  // alpha：设置坐标点与element中点的插值因数。
  function _shrink(alpha) {
    let indexList = [];
    let centerVector3 = null;
    let pointsVector3 = null;
    const faceGeometry = scene.getObjectByName(FACE_OBJ_NAME).geometry;
    const lineGeometry = scene.getObjectByName(LINE_OBJ_NAME).geometry;
    const edgeLineGeometry = scene.getObjectByName(EDGE_LINE_OBJ_NAME).geometry;
    let attributesPositionNew = attributesPosition.clone();

    // 循环所有element单元做处理
    for (const eid in elementCenterXref) {
      centerVector3 = new Vector3(
        elementCenterXref[eid][0],
        elementCenterXref[eid][1],
        elementCenterXref[eid][2]
      );
      indexList = [];
      // 面 and 边缘线
      if (elementFaceXref[eid] !== undefined) {
        // 找到该单元对应的所有顶点的index
        for (const faceId of elementFaceXref[eid]) {
          indexList.push(faceGeometry.index.getX(faceId * 3), faceGeometry.index.getY(faceId * 3), faceGeometry.index.getZ(faceId * 3));
        }
      }
      // 线
      else if (elementLineXref[eid] !== undefined) {
        // 找到该单元对应的所有顶点的index
        for (const lineId of elementLineXref[eid]) {
          indexList.push(lineGeometryIndexed.index.getX(lineId * 2), lineGeometryIndexed.index.getY(lineId * 2));
        }
      }
      // 找到每个index对应的点坐标做处理
      indexList = unique(indexList);
      indexList.forEach(index => {
        pointsVector3 = new Vector3(attributesPositionNew.getX(index), attributesPositionNew.getY(index), attributesPositionNew.getZ(index));
        pointsVector3.lerp(centerVector3, alpha);
        attributesPositionNew.setXYZ(index, pointsVector3.x, pointsVector3.y, pointsVector3.z);
      })
    }

    faceGeometry.setAttribute('position', attributesPositionNew);
    edgeLineGeometry.setAttribute('position', attributesPositionNew);
    lineGeometryIndexed.setAttribute('position', attributesPositionNew);
    lineGeometry.setPositions(lineGeometryIndexed.toNonIndexed().attributes.position.array);
  }

  // ------------------------------------------------------------------------
  // listeners
  // ------------------------------------------------------------------------

  // 事件监听：选择
  function _addSelectionEventListener() {
    window.addEventListener(MOUSE_UP_EVENT_NAME, _selectionOver);
    // 框选框
    renderer.domElement.addEventListener(MOUSE_DOWN_EVENT_NAME, _selectionBoxHelperStart);
    renderer.domElement.addEventListener(MOUSE_MOVE_EVENT_NAME, _selectionBoxHelperMove);
    window.addEventListener(MOUSE_UP_EVENT_NAME, _selectionBoxHelperOver);
    // 滑动
    renderer.domElement.addEventListener(MOUSE_MOVE_EVENT_NAME, _hoverSelectionMove);
  }

  // 移除事件监听：选择
  function _removeSelectionEventListener() {
    window.removeEventListener(MOUSE_UP_EVENT_NAME, _selectionOver);
    // 框选框
    renderer.domElement.removeEventListener(MOUSE_DOWN_EVENT_NAME, _selectionBoxHelperStart);
    renderer.domElement.removeEventListener(MOUSE_MOVE_EVENT_NAME, _selectionBoxHelperMove);
    window.removeEventListener(MOUSE_UP_EVENT_NAME, _selectionBoxHelperOver);
    // 滑动
    renderer.domElement.removeEventListener(MOUSE_MOVE_EVENT_NAME, _hoverSelectionMove);
  }

  // 选择事件
  function _selectionOver(event) {
    // console.log('屏幕坐标event.clientX:', event.clientX, '屏幕坐标event.clientY:', event.clientY);
    // console.log(
    //   'container.getBoundingClientRect().left:', container.getBoundingClientRect().left,
    //   'container.getBoundingClientRect().top:', container.getBoundingClientRect().top
    // );
    // // renderer.domElement距离屏幕左侧和上侧的距离
    // console.log(
    //   'renderer.domElement.getBoundingClientRect().left:', renderer.domElement.getBoundingClientRect().left,
    //   'renderer.domElement.getBoundingClientRect().top: ', renderer.domElement.getBoundingClientRect().top
    // );
    // console.log(
    //   'renderer.domElement.offsetLeft:', renderer.domElement.offsetLeft,
    //   'renderer.domElement.offsetTop:', renderer.domElement.offsetTop
    // );

    if (event.button === SELECT_MOUSE && scene.getObjectByName(FACE_OBJ_NAME) !== undefined) {
      // isDown防止在渲染区域外点击时进入以下代码
      if (isDown) {
        // 获取框选框的左上角和右下角屏幕坐标
        // 不使用selectionBoxElement.getBoundingClientRect()的原因：
        // 当width为0时，getBoundingClientRect()的高宽为2px（包含了border），导致container很小时无法点选
        const left = parseInt(selectionBoxElement.style.left);
        const top = parseInt(selectionBoxElement.style.top);
        const width = parseInt(selectionBoxElement.style.width);
        const height = parseInt(selectionBoxElement.style.height)
        const right = left + width;
        const bottom = top + height;

        // 屏幕坐标 转 标准化设备坐标
        const start = clientCoordinatesToNDC(left, top);
        const end = clientCoordinatesToNDC(right, bottom);

        // 获取此次选中单元
        let currentSelection = [];
        if (width + height <= CLICK_DRAG_DISTANCE) {
          //点选
          currentSelection = _getSelectionChild(end);
        } else {
          // 框选
          currentSelection = _getSelectionBoxChild(start, end);
        }

        // 获取最终选中单元
        if ((event.ctrlKey === MULTI_SELECT_KEY.ctrlKey)
          && (event.shiftKey === MULTI_SELECT_KEY.shiftKey)
          && (event.altKey === MULTI_SELECT_KEY.altKey)) {
          // 多选
          selection = selection.concat(currentSelection);
        } else if ((event.ctrlKey === DESELECT_KEY.ctrlKey)
          && (event.shiftKey === DESELECT_KEY.shiftKey)
          && (event.altKey === DESELECT_KEY.altKey)) {
          // 取消选择
          selection = selection.filter(item => !currentSelection.includes(item));
        } else {
          // 单选
          selection = currentSelection;
        }

        // 去重
        selection = unique(selection);

        // 高亮选中的单元
        _highlightSelection(selection);
        // 输出选中的单元
        outputElementSelection(selection.map(Number));
      }
    }
  }

  // 框选框开始事件
  function _selectionBoxHelperStart(event) {
    isMouseDown = true;
    if (event.button === SELECT_MOUSE) {
      isDown = true;
      renderer.domElement.parentElement.appendChild(selectionBoxElement);

      selectionBoxElement.style.left = `${event.clientX}px`;
      selectionBoxElement.style.top = `${event.clientY}px`;
      selectionBoxElement.style.width = '0px';
      selectionBoxElement.style.height = '0px';

      BoxStartPoint.x = event.clientX;
      BoxStartPoint.y = event.clientY;
    }
  }

  // 框选框移动事件
  function _selectionBoxHelperMove(event) {
    if (isDown) {
      const pointBottomRight = {
        x: Math.max(BoxStartPoint.x, event.clientX),
        y: Math.max(BoxStartPoint.y, event.clientY)
      };

      const pointTopLeft = {
        x: Math.min(BoxStartPoint.x, event.clientX),
        y: Math.min(BoxStartPoint.y, event.clientY)
      };

      selectionBoxElement.style.left = `${pointTopLeft.x}px`;
      selectionBoxElement.style.top = `${pointTopLeft.y}px`;
      selectionBoxElement.style.width = `${pointBottomRight.x - pointTopLeft.x}px`;
      selectionBoxElement.style.height = `${pointBottomRight.y - pointTopLeft.y}px`;
    }
  }

  // 框选框结束事件
  function _selectionBoxHelperOver(event) {
    BoxEndPoint.x = event.clientX;
    BoxEndPoint.y = event.clientY;
    isMouseDown = false;
    if (event.button === SELECT_MOUSE) {
      // isDown防止在渲染区域外点击时进入以下代码
      if (isDown) {
        selectionBoxElement.parentElement.removeChild(selectionBoxElement);
        isDown = false;
      }
    }
  }

  // 滑动选择移动事件
  function _hoverSelectionMove(event) {
    // 该判断条件用来处理以下bug：框选结束后，会触发该移动事件，导致框选结束点对应的单元滑动高亮
    if (!(BoxEndPoint.x == event.clientX && BoxEndPoint.y == event.clientY)) {
      // 只有鼠标没有按下情况下触发滑动高亮
      if (!isMouseDown && scene.getObjectByName(FACE_OBJ_NAME) !== undefined) {
        const start = clientCoordinatesToNDC(event.clientX, event.clientY);
        // 获取此次滑动选中单元
        let currentSelection = _getSelectionChild(start);
        // 高亮滑动的单元
        _hoverHighlight(currentSelection);
      }
    }
  }

  // 事件监听：设置旋转中心
  function _addSetRotateCenterEventListener() {
    renderer.domElement.addEventListener(MOUSE_DOWN_EVENT_NAME, _setRotateCenterStart);
    renderer.domElement.addEventListener(MOUSE_UP_EVENT_NAME, _setRotateCenterOver);
  }

  // 移除事件监听：设置旋转中心
  function _removeSetRotateCenterEventListener() {
    renderer.domElement.removeEventListener(MOUSE_DOWN_EVENT_NAME, _setRotateCenterStart);
    renderer.domElement.removeEventListener(MOUSE_UP_EVENT_NAME, _setRotateCenterOver);
  }

  // 设置旋转中心时，鼠标点击按下事件
  function _setRotateCenterStart(event) {
    if (event.button === SET_ROTATE_CENTER_MOUSE) {
      startPoint.x = event.clientX;
      startPoint.y = event.clientY;
    }
  }

  // 设置旋转中心时，鼠标点击结束事件
  function _setRotateCenterOver(event) {
    // 只有鼠标右键单击可以设置旋转中心
    if (event.button === SET_ROTATE_CENTER_MOUSE) {
      // 区分点击和拖动
      if (Math.abs(startPoint.x - event.clientX) + Math.abs(startPoint.y - event.clientY) <= CLICK_DRAG_DISTANCE) {
        console.log('设置旋转中心');
        // 屏幕坐标 转 标准化设备坐标
        const mouse = clientCoordinatesToNDC(event.clientX, event.clientY);
        // 将点击选中的element的中心设置为旋转中心
        raycaster.setFromCamera(mouse, camera);
        const intersects = raycaster.intersectObjects([scene.getObjectByName(FACE_OBJ_NAME), scene.getObjectByName(LINE_OBJ_NAME)]);
        if (intersects.length > 0) {
          currentRotateCenter = intersects[0].point;
          cameraSpinControl.setTargetPosition(currentRotateCenter);
        }
      }
    }
  }

  // 屏幕变化事件
  function _onWindowResize() {
    if (container.clientWidth !== 0 && container.clientHeight !== 0) {
      // 重新设置相机向下左右侧面，保证模型不变形
      camera.left = container.clientWidth / -2;
      camera.right = container.clientWidth / 2;
      camera.top = container.clientHeight / 2;
      camera.bottom = container.clientHeight / -2;
      camera.updateProjectionMatrix();

      // 添加setTimeout是防止有时候canvas大小变化时闪现黑屏
      setTimeout(() => {
        // 重新设置render的size，第三个参数设置为false以阻止对canvas的样式做任何改变
        renderer.setSize(container.clientWidth, container.clientHeight, false);
        // 重新设置旋转控制器
        cameraSpinControl.onWindowResize();
      });
    }
    // console.log('renderer:', renderer.domElement.width, renderer.domElement.height);
    // console.log('container:', container.clientWidth, container.clientHeight);
  }
}

// 单例模式
createThree.getInstance = (function () {
  let instance = null;
  return function (container, outputElementSelection) {
    if (!instance) {
      instance = createThree(container, outputElementSelection);
    } else {
      instance.applyArguments(container, outputElementSelection);
    }
    return instance;
  }
})()

export { createThree };
